package com.ideal.tools.ssh.entity;


import com.ideal.tools.ssh.context.ClusterContext;
import com.ideal.tools.ssh.executor.SCPDownLoadExecutor;
import com.ideal.tools.ssh.executor.SSHExecutor;
import com.ideal.tools.ssh.operation.LinuxOperation;
import com.ideal.tools.ssh.result.LinuxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * Created by CC on 2016/3/1.
 */
public class LinuxMachine {
    String ip;
    String loginname;
    String password;
    String publicKey;       //机器对应用户的令牌
    List<LinuxOperation> operations;       //机器的操作集合
    SSHExecutor sshExecutor ;       //操作的执行器

    SSHAuthor sshAuthor;          //机器的认证     这是本机的认证
    ClusterContext clusterContext;    //上下文环境
    List<LinuxResult> resultList; //运行的结果集

    MachineType machineType;     //这个是本机的属性标志

    List<MachineType> machineRoleTypes; //这个是这个机器需要承担的角色

    int executOrder ; //机器的执行顺序

    boolean canConnectHost = true;
    private static Logger logger = LoggerFactory.getLogger(LinuxMachine.class);
    public LinuxMachine(){
        resultList=new ArrayList<LinuxResult>();
        sshAuthor = new SSHAuthor();
    }

    public int getExecutOrder(){
        return executOrder;
    }

    public void setExecutOrder(int order){
        this.executOrder=order;
    }

    public LinuxMachine initIP(String ip){
        this.ip=ip;
        sshAuthor.setHost(ip);
        return this;
    }

    public LinuxMachine initLoginName(String name){
        this.loginname = name;
        sshAuthor.setUsername(name);
        return this;
    }

    public LinuxMachine initPassWord(String password){
        this.password=password;
        sshAuthor.setPasswd(password);
        return this;
    }

    public LinuxMachine initPubKey(String publicKey){
        this.publicKey=publicKey;
        sshAuthor.setPubToken(publicKey);
        return this;
    }

    public LinuxMachine initOperations(List<LinuxOperation> operationList){
        //这是一个有 就合并 没有就 替换的操作
        if(this.operations!=null){
            this.operations.addAll(operationList);
        }else {
            this.operations = operationList;
        }
        return this;
    }

    public int getOperationsSize(){
        return operations==null?0:operations.size();
    }

    /**
     * 默认可执行多次
     * @param linuxOperation
     * @return
     */
    public LinuxMachine initOperation(LinuxOperation linuxOperation){
        return initOperation(linuxOperation,false);
    }

    /**
     * 这个方法在初始 是否执行多次
     * @param linuxOperation
     * @param onlyOnce
     * @return
     */
    public LinuxMachine initOperation(LinuxOperation linuxOperation,boolean onlyOnce){

        if(this.operations ==null){
            this.operations = new ArrayList<LinuxOperation>() ;
        }

        this.operations.add(linuxOperation);

        //这里需要记录一下 机器的类型 和 机器执行的哪些操作
        if(onlyOnce){

            clusterContext.setOnlyOneceOperation(this,linuxOperation);
        }

        return this;
    }

    public LinuxMachine initLinuxContext(ClusterContext context){
        this.clusterContext =context;
        return this;
    }

    public LinuxMachine initMachineType(MachineType machineType){
        this.machineType= machineType;
        return this;
    }

    public ClusterContext getClusterContext() {
        return clusterContext;
    }

    /**
     * 根据操作类型
     *
     * @return
     */
    public LinuxResult connectMachine(LinuxOperation linuxOperation) {
        LinuxResult result= null;
        if(linuxOperation.getOperatType() == LinuxOperation.OperationType.EXE_CMD){
            result = initMachineConnect(linuxOperation);
            logger.warn("###"+result.getNote());
        } else if(linuxOperation.getOperatType() == LinuxOperation.OperationType.SCP_DOWNLOAD){
            result = initFromMachineConnect(linuxOperation);
        }
        return result;
    }

    /**
     * 自己本机的连接 一直都保持一个
     * 连接机器 这个地方 是创建的长连接 一个机器只需要创建一次
     * @param linuxOperation
     * @return
     */
    public LinuxResult initMachineConnect(LinuxOperation linuxOperation){
        LinuxResult result= null;
        if(sshExecutor==null) {
            sshExecutor = new SSHExecutor();
            //初始化 本机的登陆认证
            initMachineAuth(ip,loginname, password,publicKey);
            logger.debug("###Auth info: ip->"+ip+" logingname->"+loginname+" password->"+password +" publickKey->"+publicKey);
            logger.debug("###sshAuthor:host->"+sshAuthor.getHost()+" passwd->"+sshAuthor.getPasswd()+" username->"+sshAuthor.getUsername()+ " pubtoken"+sshAuthor.getPubToken());
            sshExecutor.setSshAuthor(sshAuthor);
            logger.debug("###sshExecutor "+sshExecutor.getSshAuthor().getHost()+" "+sshExecutor.getSshAuthor().getPasswd()+" "+sshExecutor.getSshAuthor().getUsername()+" "+sshExecutor.getSshAuthor().getPubToken());
            result = sshExecutor.initSSHConnect();

            //对于错误的执行器  这里需要还原
            /******************modify 2016 04 11 cc 由于失败后会尝试多次连接
             *              一台机器就只有个连接器，如果初始不成功就不在尝试
             *********************************************************/
//            if(result.getExitCode()==LinuxResult.DEFAULT_FAILD_CODE){
//                sshExecutor.closeConnection();
//                sshExecutor=null;
//            }
            if(result.getExitCode()==LinuxResult.DEFAULT_FAILD_CODE){
                canConnectHost=false;
            }
        }else{
            result = sshExecutor.initSuccResult("USER[" + sshAuthor.getUsername() +
                        "] login in HOST[" + sshAuthor.getHost() + "] SUCCESS!");
        }
        return result;
    }

    /**
     * 下载的 执行器每次都 拿新的
     * @param linuxOperation
     * @return
     */
    public LinuxResult initFromMachineConnect(LinuxOperation linuxOperation){
        SCPDownLoadExecutor scpDownLoadExecutor=new SCPDownLoadExecutor();
        //初始化
        scpDownLoadExecutor.setSshAuthor(linuxOperation.getFromSSHAuthor());
        LinuxResult result = scpDownLoadExecutor.initSSHConnect();

        return result;
    }



//    public


    /**
     * 关闭连接
     */
    public void closeMachineConnection(){
        //这里需要判断是否为null 因为很可能是 连接没有成功
        if(sshExecutor!=null)
            sshExecutor.closeConnection();
    }

    /**
     * 这里我想要的逻辑是 不管每一步的执行是否成功 我们都向下执行
     * 应为
     */
    public void doOperation(){
        if(operations == null)
            return;
        for(LinuxOperation operation:operations){
            //只执行一次的操作 不能重复执行
            //对于连接不上的 直接跳过
            if(clusterContext.isOnlyOnceOperation(this,operation)||!canConnectHost)
                continue;

            operation.execLinuxCMD(clusterContext);
            LinuxResult tmpResult = operation.getExeResult();
            resultList.add(tmpResult);
        }
    }

    /**
     * 这个地方会获取所有的 运行结果
     * 这里我们在获取结果之后 就关闭连接
     * @return
     */
    public List<LinuxResult> getResult(){
        closeMachineConnection();
        return resultList;
    }

    /**
     * 初始化 本机器
     * @param host
     * @param user
     * @param password
     * @param pubKey
     */
    public void initMachineAuth(String host,String user,String password,String pubKey){
        if(sshAuthor==null){
            sshAuthor=new SSHAuthor();
        }
        initSSHAuthor(sshAuthor,host,user,password,pubKey);
    }

    private SSHAuthor initSSHAuthor(SSHAuthor sshAuthor,String host,String user,String password,String pubKey){
        if(sshAuthor==null){
            sshAuthor=new SSHAuthor();
        }
        sshAuthor.setHost(host);
        sshAuthor.setUsername(user);
        sshAuthor.setPasswd(password);
        sshAuthor.setPubToken(pubKey);
        return sshAuthor;
    }

    public SSHAuthor getSshAuthor() {
        return sshAuthor;
    }

    public void setSshAuthor(SSHAuthor sshAuthor) {
        this.sshAuthor = sshAuthor;
        this.ip = sshAuthor.getHost();
        this.loginname = sshAuthor.getUsername();
        this.password = sshAuthor.getPasswd();
        this.publicKey = sshAuthor.getPubToken();
    }

    public MachineType getMachineType() {
        return machineType;
    }

    public void setMachineType(MachineType machineType) {
        this.machineType = machineType;
    }

    public List<MachineType> getMachineRoleTypes() {
        return machineRoleTypes;
    }

    public void setMachineRoleTypes(List<MachineType> machineRoleTypes) {
        this.machineRoleTypes = machineRoleTypes;
    }

    /**
     * 这个用来确定 机器的不同类型
     * 对于同一个操作 不同的机器有不同的反应
     */
      public  enum MachineType{
        NameNode("NN"),DataNode("DN"),Client("Client"),KDC("KDC"),Hive("Hive"),ClushShell("Clushell"),ResourceManage("RM"),WebAPP("WebApp");
        private String value;
        MachineType(String value) {
            this.value = value;
        }
        private String getValue(){
            return value;
        }
        //通过值来获取枚举类型
        public static MachineType getEnumMachineType(String value){
            for(MachineType machineType:MachineType.values()){
                if(value.equals(machineType.getValue())){
                    return machineType;
                }
            }
            return  null;
        }
    }
    /**
     * 判断一台机器是否和另外一台 一样
     * @param machine
     * @return
     */
    public boolean isTheSameMachine(LinuxMachine machine){
        if(machine.getSshAuthor().getHost().equals(sshAuthor.getHost())&&
           machine.getSshAuthor().getUsername().equals(sshAuthor.getUsername())&&
           machine.getMachineType()==machineType)
            return true;
        return false;
    }

    public String getMachineSingle(){
        return loginname+"@"+ip;
    }


    public void clearMachine(){
        //清空所有操作
        if(operations!=null) {
            this.operations.clear();
        }
        //清空机器类型
        if(machineRoleTypes!=null) {
            machineRoleTypes.clear();
        }
        //清空结果
        if(resultList!=null) {
            resultList.clear();
        }
        //清空连接  这里手动去除，感觉有点不妥。后面看到，再想想
        if(sshExecutor!=null){
            sshExecutor=null;
        }
    }
}
